﻿using System;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows.Threading;
using DataStore;
using DataStore.Domain;
using DataStore.Repositories;
using System.Windows.Input;
using Infrastructure.CurrentData;
using Microsoft.Practices.Composite.Events;

namespace Infrastructure.ViewModels
{
    /// <summary>
    /// All catalog items in database
    /// </summary>
    public class AllCatalogItemsViewModel : DataModel
    {
        
        /// <summary>
        /// Catalog items in database
        /// </summary>
        ObservableCollection<CatalogItemViewModel> _catalogItems = new ObservableCollection<CatalogItemViewModel>();

        /// <summary>
        /// Initializes a new instance of the <see cref="AllCatalogItemsViewModel"/> class.
        /// </summary>
        public AllCatalogItemsViewModel() : base()
        {
            Initialize();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AllCatalogItemsViewModel"/> class.
        /// </summary>
        /// <param name="eventAggregator">The event aggregator.</param>
        public AllCatalogItemsViewModel(IEventAggregator eventAggregator) : base(eventAggregator)
        {
            Initialize();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AllCatalogItemsViewModel"/> class.
        /// </summary>
        /// <param name="eventAggregator">The event aggregator.</param>
        /// <param name="unitOfWork">The unit of work.</param>
        public AllCatalogItemsViewModel(IEventAggregator eventAggregator, UnitOfWork unitOfWork) : base(eventAggregator)
        {
            Initialize();
        }

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        void Initialize()
        {
            State = ModelState.Fetching;

            RefreshCatalogItems();
        }

        /// <summary>
        /// Refreshes the catalog items.
        /// </summary>
        public void RefreshCatalogItems()
        {
            if (!ThreadPool.QueueUserWorkItem(FetchCatalogItemsCallback))
            {
                State = ModelState.Invalid;
            }
        }

        /// <summary>
        /// Gets or sets the catalog items.
        /// </summary>
        /// <value>The catalog items.</value>
        public ObservableCollection<CatalogItemViewModel> CatalogItems
        {
            get
            {
                VerifyCalledOnUIThread();
                return _catalogItems;
            }
            private set
            {
                VerifyCalledOnUIThread();
                if (_catalogItems != value)
                {
                    _catalogItems = value;
                    OnPropertyChanged("CatalogItems");
                }
            }
        }


        /// <summary>
        /// Fetches the catalog items callback.
        /// </summary>
        /// <param name="state">The state.</param>
        private void FetchCatalogItemsCallback(object state)
        {
            IDatabase database = Current.Container.Resolve<IDatabase>();
            var unitOfWork = database.CreateUnitOfWork();

            try
            {
                
                //IRepository<CatalogItem> catalogItemRepository = new CatalogItemRepository(unitOfWork);
                IRepository<Book> bookRepository = new BookRepository(unitOfWork);

                //var catalogItems = catalogItemRepository.All;
                var books = bookRepository.All;

                Dispatcher.Invoke(
                    new ThreadStart(delegate
                                        {
                                            CatalogItems.Clear();
                                            //foreach (var book in catalogItems)
                                            //{
                                            //    CatalogItems.Add(new CatalogItemViewModel(book));
                                            //}
                                            foreach (Book book in books)
                                            {
                                                CatalogItems.Add(new BookViewModel(EventAggregator, book));
                                            }

                                            unitOfWork.Dispose();
                                            State = ModelState.Active;


                                            OnLoaded(this, EventArgs.Empty);
                                            OnPropertyChanged("CatalogItems");
                                        }),
                    DispatcherPriority.Normal);
            }
            catch (Exception e)
            {
                Log.Error("Failed to load CatalogItems", e);

                unitOfWork.Dispose();
                Dispatcher.BeginInvoke(
                    new ThreadStart(delegate
                                        {
                                            State = ModelState.Invalid;
                                        }),
                    DispatcherPriority.Normal);
                
            }
        }

        /// <summary>
        /// Selected item.
        /// </summary>
        CatalogItemViewModel _selected;

        /// <summary>
        /// Gets or sets the selected.
        /// </summary>
        /// <value>The selected.</value>
        /// <exception cref="ArgumentException">Non-existing viewmodel selected.</exception>
        public CatalogItemViewModel Selected
        {
            get
            {
                return _selected;
            }
            set
            {
                if(_catalogItems.Contains(value) || value == null)
                {
                    _selected = value;
                    OnPropertyChanged("Selected");
                }
                else
                {
                    throw new ArgumentException("Non-existing viewmodel selected.");
                }
            }
        }

        /// <summary>
        /// Gets the selected.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetSelected<T>()
        {
            return (T)Convert.ChangeType(Selected, typeof (T));
        }

        /// <summary>
        /// Delete relay command
        /// </summary>
        RelayCommand _deleteSelectedCommand;

        /// <summary>
        /// Gets the 'delete selected item' command.
        /// </summary>
        /// <value>The 'delete selected item' command.</value>
        public ICommand DeleteSelectedCommand
        {
            get
            {
                if(_deleteSelectedCommand == null)
                {
                    _deleteSelectedCommand = new RelayCommand(
                        param => DeleteSelected(),
                        param => CanDeleteSelected);
                }
                return _deleteSelectedCommand;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the selected item can be deleted.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance can delete selected; otherwise, <c>false</c>.
        /// </value>
        protected bool CanDeleteSelected
        {
            get { return Selected != null; }
        }

        /// <summary>
        /// Deletes the selected item.
        /// </summary>
        void DeleteSelected()
        {
            Selected.DeleteCommand.Execute(null);
            _catalogItems.Remove(Selected);
            Selected = null;
        }
    }
}